package com.aotain.oframework.util.mail;

import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.mail.EmailAttachment;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.HtmlEmail;
import org.apache.log4j.BasicConfigurator;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.RuntimeConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ui.velocity.VelocityEngineUtils;
import org.springframework.util.Assert;

public class EmailHelper {
	private Logger logger = LoggerFactory.getLogger(this.getClass());
	private EmailConfig emailConfig;
	private VelocityEngine velocityEngine;
	
	public EmailHelper(EmailConfig emailConfig){
		this.emailConfig = emailConfig;
		this.velocityEngine = getVelocityEngine(emailConfig);
	}

	public VelocityEngine getVelocityEngine(EmailConfig emailConfig){
		VelocityEngine engine = new VelocityEngine();
		/* first, we init the runtime engine.   Defaults are fine. */  
		Properties p = new Properties();   
		//设置输入输出编码类型。和这次说的解决的问题无关   
		p.setProperty(RuntimeConstants.INPUT_ENCODING, "UTF-8");   
		p.setProperty(RuntimeConstants.OUTPUT_ENCODING, "UTF-8");   
		
		//这里加载classpath里的模板   
		//p.setProperty("resource.loader", "class");
		p.setProperty("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.FileResourceLoader ");   
		p.setProperty("runtime.log.logsystem.class", "org.apache.velocity.runtime.log.SystemLogChute");
		//也可以用下面方法指定一个绝对路径，不过这样要求你所有的模板都放在该路径下，是有局限的   
		p.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, this.emailConfig.getTemplateFolder());   
		logger.debug("template path = {}", this.emailConfig.getTemplateFolder());
		try {
			engine.init(p);
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("error in initiating velocity: {}", e);
		}
		return engine;

	}

	private void updateEmailContent(HtmlEmail email, EmailParam emailParam, List<EmailAttachment> mailAttachments) throws EmailException{
		Assert.notNull(emailParam, "Email Param can not be null.");
		//Assert.notNull(emailParam.getToEmail(), "To Email can not be null.");
		//Assert.notNull(emailParam.getToContacts(), "To Email can not be null.");
		logger.debug("To Name : [" + emailParam.getToName() + "]");
		logger.debug("To Email : [" + emailParam.getToEmail() + "]");
		logger.debug("To Contacts : [" + emailParam.getToContacts() + "]");
		logger.debug("Cc Contacts : [" + emailParam.getCcContacts() + "]");
		logger.debug("From Name : [" + emailParam.getFormName() + "]");
		logger.debug("From Email : [" + emailParam.getFromMail() + "]");
		logger.debug("Email Subject : [" + emailParam.getSubject() + "]");
		//attach files
		if(mailAttachments!=null && mailAttachments.size()>0){
			for(EmailAttachment att: mailAttachments){
				logger.debug("Addding attachment: [{}]", att.getPath());
				email.attach(att);
			}
		}
		
		//set from/to/mail content
		if(emailParam.getToEmail()!=null){
			email.addTo(emailParam.getToEmail(), emailParam.getToName());
		}else if(emailParam.getToContacts()!=null){
			for(EmailContact ec:emailParam.getToContacts()){
				email.addTo(ec.getToEmail(), ec.getToName());
			}
			if(emailParam.getCcContacts()!=null){
				for(EmailContact ec:emailParam.getCcContacts()){
					email.addCc(ec.getToEmail(), ec.getToName());
				}
			}
		}else{
			throw new IllegalArgumentException("You must specify one or more sender Tos(use emailParam.setToEmail or emailParam.setToContacts).");
		}
		
		email.setHtmlMsg(this.getMessage(emailParam));
		email.setSubject(emailParam.getSubject());
		
		if(emailParam.getFromMail() != null){
			email.setFrom(emailParam.getFromMail(), emailParam.getFormName());
		}
		

	}

	private void sendEmailWithAttachment(EmailParam emailParam, List<EmailAttachment> mailAttachments) throws EmailException{
		
		String result = null;
		try{
			logger.debug("using primary server to send email.");
			HtmlEmail email = new HtmlEmail();
			updateEmailContent(email, emailParam, mailAttachments);//add at 2013/4/3
			updateServerCofig(email, this.emailConfig, true);
			result = email.send();
		}catch(Exception e){
			e.printStackTrace();
			logger.error("error in sending mail with primary server {}", this.emailConfig.getPrimarySMTPHost());
			logger.debug("using secondary server to send email.");
			try {
				HtmlEmail email = new HtmlEmail();
				updateEmailContent(email, emailParam, mailAttachments);
				updateServerCofig(email, this.emailConfig, false);
				result = email.send();
			} catch (Exception e2) {
				logger.error("error in sending mail with secondary server {}", this.emailConfig.getSecondarySMTPHost());
				throw new EmailException("Sending email failed.", e2);
			}
		} finally{
			if(result!=null){
				logger.info("Email was sent successfully({}) !!!, To[{}] , Tos[{}] , Ccs[{}]", new Object[]{result,emailParam.getToEmail(),emailParam.getToContacts(),emailParam.getCcContacts()});
			}
		}
		
	}
	public void sendEmailWithUrl(EmailParam emailParam, List<URL> urls) throws EmailException{
		List<EmailAttachment> mailAttachments = new ArrayList<EmailAttachment>();
		//attach files
		if(urls!=null && urls.size()>0){
			for(URL url: urls){
				EmailAttachment attachment = new EmailAttachment();
				attachment.setURL(url);
				attachment.setName(url.toExternalForm());
				attachment.setDisposition(EmailAttachment.ATTACHMENT);
				mailAttachments.add(attachment);
			}
		}
		sendEmailWithAttachment(emailParam, mailAttachments);
	}
	
	public void sendEmail(EmailParam emailParam, List<File> files) throws EmailException{
		List<EmailAttachment> mailAttachments = new ArrayList<EmailAttachment>();
		//attach files
		if(files!=null && files.size()>0){
			for(File file: files){
				EmailAttachment attachment = new EmailAttachment();
				attachment.setName(file.getName());
				attachment.setPath(file.getAbsolutePath());
				attachment.setDisposition(EmailAttachment.ATTACHMENT);
				mailAttachments.add(attachment);
			}
		}
		sendEmailWithAttachment(emailParam, mailAttachments);
	}
//	public void sendEmail(EmailParam emailParam, List<URL> urls) throws EmailException{
//		EmailAttachment attachment = new EmailAttachment();
//		attachment.setDisposition(EmailAttachment.ATTACHMENT);
//	}
	
	public void sendEmail(EmailParam emailParam) throws EmailException{
		logger.debug("Sending email without attachment.");
		this.sendEmail(emailParam, null);
	}

	private String getMessage(EmailParam emailParam) {
		if(emailParam.getEmailContent()==null){
			Assert.notNull(emailParam.getVmLocation(), "Template Location can not be null.");
			Assert.notNull(emailParam.getVmMap(), "Data Model for VM can not be null.");
			String templateLocation = emailParam.getVmLocation();
			String content = VelocityEngineUtils.mergeTemplateIntoString(velocityEngine, templateLocation, "UTF-8", emailParam.getVmMap());
			logger.debug("*****************Main Content(VM)********************\n"+content);
			return content;
		}else{
			logger.debug("*****************Main Content(String)********************\n"+emailParam.getEmailContent());
			return emailParam.getEmailContent();
		}
	}

	private void updateServerCofig(HtmlEmail email, EmailConfig emailConfig, boolean isPrimary) throws EmailException {
		if(email.getFromAddress()==null){
			email.setFrom(emailConfig.getMailForm(), emailConfig.getMailFormName());
		}
		logger.debug("Default From Name : [" + emailConfig.getMailForm() + "]");
		logger.debug("Default From Email : [" + emailConfig.getMailFormName() + "]");
		email.setCharset(emailConfig.getCharset());
		if(isPrimary){
			
			email.setHostName(emailConfig.getPrimarySMTPHost());
			email.setSmtpPort(Integer.valueOf(emailConfig.getPrimarySMTPPort()));
		}else{
			
			email.setHostName(emailConfig.getSecondarySMTPHost());
			email.setSmtpPort(Integer.valueOf(emailConfig.getSecondarySMTPPort()));
		}
		logger.debug("Email Host Name : [" + email.getHostName() + "]");
		logger.debug("Email Host Port : [" + email.getSmtpPort() + "]");
		if(emailConfig.getRequiredAuth()){
			email.setAuthentication(emailConfig.getEmailLoginId(), emailConfig.getEmailLoginPassword());
		}
	}

	public static void main(String[] args) {
		BasicConfigurator.configure();
		
		EmailConfig emailConfig = new EmailConfig();
		emailConfig.setCharset("UTF-8");
		emailConfig.setEmailLoginId("william6117@163.com");
		emailConfig.setMailForm("william6117@163.com");
		emailConfig.setMailFormName("chengwei.zhang");
		emailConfig.setEmailLoginPassword("zhangchengwei");
		emailConfig.setPrimarySMTPHost("smtp.163.com");
		emailConfig.setPrimarySMTPPort("25");
		
		emailConfig.setSecondarySMTPHost("smtp.163.com");
		emailConfig.setSecondarySMTPPort("25");
		emailConfig.setRequiredAuth(true);
		emailConfig.setTemplateFolder("");
		EmailHelper helper = new EmailHelper(emailConfig);
		Map<String, Object> vmMap = new HashMap<String, Object>();
		vmMap.put("cnm", "Lee1");
		vmMap.put("enm", "Lee2");
		
		EmailContact e1=new EmailContact("zhangchengwei", "407917542@qq.com");
		List<EmailContact> es=new ArrayList<EmailContact>();
		es.add(e1);
		EmailParam emailParam1 = new EmailParam(es,null,"Notification of Write-off Application Withdrawal.vm","hello",vmMap);
		try {
			helper.sendEmail(emailParam1);
		} catch (Exception e) {
			e.printStackTrace();
		}

	}


}
